package com.wjm.myhookpluginloadedapk;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;

import java.io.File;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

import dalvik.system.DexClassLoader;

public class HookUtil {
    private Context context;

    public void hookStartActivity(Context context) {
        this.context = context;
        try {
            Log.d("TAG","hookStartActivity 1");
            Class<?> ActivityManagerNativecls = Class.forName("android.app.ActivityManagerNative");
            Field gDefault = ActivityManagerNativecls.getDeclaredField("gDefault");
            gDefault.setAccessible(true);
            //            因为是静态变量  所以获取的到的是系统值  hook   伪hook
            Object defaltValue = gDefault.get(null);
            Class<?> singletonClass = Class.forName("android.util.Singleton");
            Field mInstance = singletonClass.getDeclaredField("mInstance");
            mInstance.setAccessible(true);
            Object iActivityManagerObject = mInstance.get(defaltValue);
            Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
            StartActivty startActivty = new StartActivty(iActivityManagerObject);
            Object oldIactivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IActivityManagerIntercept, View.OnClickListener.class}, startActivty);
            mInstance.set(defaltValue, oldIactivityManager);
            Log.d("TAG","hookStartActivity 2");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    class StartActivty implements InvocationHandler {

        private Object iActivityManagerObject;

        public StartActivty(Object iActivityManagerObject) {
            this.iActivityManagerObject = iActivityManagerObject;
        }

        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            if ("startActivity".equals(method.getName())) {
                Log.d("TAG","StartActivty invoke if");
                Intent intent = null;
                int index = 0;
                for (int i = 0; i < objects.length; i++) {
                    Object object = objects[i];
                    if (object instanceof Intent) {
                        intent = (Intent) objects[i];
                        index = i;
                    }
                }
                Intent newIntent = new Intent();
                ComponentName componentName = new ComponentName(context, ProxyActivity.class);
                newIntent.setComponent(componentName);
                newIntent.putExtra("oldIntent", intent);
                objects[index] = newIntent;
            }
            Log.d("TAG","StartActivty invoke");
            return method.invoke(iActivityManagerObject, objects);
        }
    }

    public void hookHookMh(Context context) {
        try {
            Log.d("TAG","hookHookMh 1");
            Class<?> aClass = Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThread = aClass.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThread.setAccessible(true);
            Object activityThreadObj = sCurrentActivityThread.get(null);
            Field handlerField = aClass.getDeclaredField("mH");
            handlerField.setAccessible(true);
            Handler mH = (Handler) handlerField.get(activityThreadObj);
            Field mCallback = Handler.class.getDeclaredField("mCallback");
            mCallback.setAccessible(true);
            mCallback.set(mH,new ActivityMH(mH));
            Log.d("TAG","hookHookMh 2");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    class ActivityMH implements Handler.Callback{
        private Handler mH;

        public ActivityMH(Handler mH) {
            this.mH=mH;
        }

        @Override
        public boolean handleMessage(Message message) {
            if(message.what ==100){
                handleLuachActivity(message);
            }
            mH.handleMessage(message);
            return true;
        }

        private void handleLuachActivity(Message message) {
            Object obj = message.obj;
            try {
                Field intent = obj.getClass().getDeclaredField("intent");
                intent.setAccessible(true);
                Intent realyIntent = (Intent) intent.get(obj);
                Intent oldIntent = realyIntent.getParcelableExtra("oldIntent");
              /*  if(null!=oldIntent){
                    SharedPreferences share = context.getSharedPreferences("david", Context.MODE_PRIVATE);

                    // 集中式登录
                    if(share.getBoolean("login",false)){
//                    if(share.getBoolean("login",false)||oldIntent.getComponent().getClassName().equals(SceondActivity.class.getName())){
                        Log.d("TAG","true 登录了");
                        realyIntent.setComponent(oldIntent.getComponent());
                    }else{
                        Log.d("TAG","false 还未登录");
                        ComponentName componentName = new ComponentName(context, LoginActivity.class);
                        realyIntent.putExtra("extraIntent",oldIntent.getComponent().getClassName());
                        realyIntent.setComponent(componentName);
                    }

                }*/

                if (oldIntent != null) {
                    App.isPlugin = true;
//                    集中式登录
                    SharedPreferences share = context.getSharedPreferences("wjm",
                            Context.MODE_PRIVATE);
                    if (share.getBoolean("login",false)) {

//                      登录  还原  把原有的意图    放到realyIntent   SecondeActivity
                        realyIntent.setComponent(oldIntent.getComponent());
                    }else {//不是插件
                        App.isPlugin = false;
                        ComponentName componentName = new ComponentName(context,LoginActivity.class);
                        realyIntent.putExtra("extraIntent", oldIntent.getComponent()
                                .getClassName());
                        realyIntent.setComponent(componentName);
                    }
//                    if(share.getBoolean("login",false)){//登录了
                        realyIntent.setComponent(oldIntent.getComponent());
                        Field activityInfoField= obj.getClass().getDeclaredField("activityInfo");
                        activityInfoField.setAccessible(true);
                        ActivityInfo activityInfo= (ActivityInfo) activityInfoField.get(obj);
//              插件的class  packageName--->loadeApk   系统   第一次 IPackageManager ----》activitry  -——》包名   ---》
//                    不够 IPackageManage.getPackageInfo()
                        activityInfo.applicationInfo.packageName = oldIntent.getPackage() == null ? oldIntent.getComponent().getPackageName()
                                : oldIntent.getPackage();

                        //          hook   方法  IPackageManager.getPackgeInfo
                        // 这一步是因为 initializeJavaContextClassLoader 这个方法内部无意中检查了这个包是否在系统安装
                        // 如果没有安装, 直接抛出异常, 这里需要临时Hook掉 PMS, 绕过这个检查.
                        hookPackgeManager();
//                    }



                }else {
                    App.isPlugin = false;
                }
//              msg.obj      真实类型   ActivityClientRecord
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }


    private void hookPackgeManager() {
//          hook   方法  IPackageManager.getPackgeInfo

        // 这一步是因为 initializeJavaContextClassLoader 这个方法内部无意中检查了这个包是否在系统安装
        // 如果没有安装, 直接抛出异常, 这里需要临时Hook掉 PMS, 绕过这个检查.

        Class<?> activityThreadClass = null;
        try {
            activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            Object currentActivityThread = currentActivityThreadMethod.invoke(null);

            // 获取ActivityThread里面原始的 sPackageManager
            Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
            sPackageManagerField.setAccessible(true);
            Object sPackageManager = sPackageManagerField.get(currentActivityThread);

            Log.i("david", " handleMessage之前发生啦   ");
            // 准备好代理对象, 用来替换原始的对象
            Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager");
            Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader()
                    , new Class[]{iPackageManagerInterface},new IPackageManagerHandler(sPackageManager) );
            sPackageManagerField.set(currentActivityThread,proxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void injectPluginClass() {
        String absolutePath = context.getCacheDir().getAbsolutePath();
        String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + App.apkName;
        DexClassLoader dexClassLoader = new DexClassLoader(apkPath, absolutePath, absolutePath, context.getClassLoader());

        try {
            //1插件的Elements[]数组
            Class<?> myDexClazzLoader = Class.forName("dalvik.system.BaseDexClassLoader");
            Field myPathListField = myDexClazzLoader.getDeclaredField("pathList");
            myPathListField.setAccessible(true);
            Object myPathListObject = myPathListField.get(dexClassLoader);
            Class<?> myPathClazz = myPathListObject.getClass();
            Field dexElementsField = myPathClazz.getDeclaredField("dexElements");
            dexElementsField.setAccessible(true);
            //自已插件的dexElements[]
            Object myElements = dexElementsField.get(myPathListObject);

            //2系统的Elements数组
            ClassLoader pathClassLoader = context.getClassLoader();
            Class<?> baseDexClazzLoader = Class.forName("dalvik.system.BaseDexClassLoader");
            Field pathListField = baseDexClazzLoader.getDeclaredField("pathList");
            pathListField.setAccessible(true);
            Object pathListObject = pathListField.get(pathClassLoader);
            Class<?> systemPathClazz = pathListObject.getClass();
            Field systemElementsField = systemPathClazz.getDeclaredField("dexElements");
            systemElementsField.setAccessible(true);
            //系统的dexElements[]
            Object systemElements = systemElementsField.get(pathListObject);

            //3将以上二个数组合并
            int systemLength = Array.getLength(systemElements);
            int myLength = Array.getLength(myElements);
            Class<?> componentType = systemElements.getClass().getComponentType();
            int newSystemLength = systemLength + myLength;
            Object newElementsArray = Array.newInstance(componentType, newSystemLength);
            for(int i=0;i<newSystemLength;i++){
                if(i<myLength){
                    Array.set(newElementsArray,i,Array.get(myElements,i));
                }else{
                    Array.set(newElementsArray,i,Array.get(systemElements,i-myLength));
                }
            }
            //通过pathListObject对象再将新生成的Elements数组对象重新放到系统中去
            Field dexElements = pathListObject.getClass().getDeclaredField("dexElements");
            dexElements.setAccessible(true);
            dexElements.set(pathListObject,newElementsArray);


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


//    通过  修改  mPackages   ArrayMap<String, WeakReference<LoadedApk>>
//    LoadedApk ---->插件
//    2   实例化 LoadedApk
//    3   put
    public void putLoadedApk(String path) {
        try {

            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
//            先还原activityThread对象
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            Object currentActivityThread = currentActivityThreadMethod.invoke(null);

//    再来还原  mPackages  对象
            // 获取到 mPackages 这个静态成员变量, 这里缓存了apk包的信息
            Field mPackagesField = activityThreadClass.getDeclaredField("mPackages");
            mPackagesField.setAccessible(true);
            Map mPackages = (Map) mPackagesField.get(currentActivityThread);

//z找到  getPackageInfoNoCheck   method 方法
            Class<?> compatibilityInfoClass = Class.forName("android.content.res.CompatibilityInfo");
            Method getPackageInfoNoCheckMethod = activityThreadClass.getDeclaredMethod(
                    "getPackageInfoNoCheck", ApplicationInfo.class, compatibilityInfoClass);

//        得到 CompatibilityInfo  里面的  静态成员变量       DEFAULT_COMPATIBILITY_INFO  类型  CompatibilityInfo
            Field defaultCompatibilityInfoField = compatibilityInfoClass.getDeclaredField("DEFAULT_COMPATIBILITY_INFO");
            Object defaultCompatibilityInfo = defaultCompatibilityInfoField.get(null);

            ApplicationInfo applicationInfo = parseReceivers(context, path);

//            一个问题  传参 ApplicationInfo ai 一定是与插件相关    ApplicationInfo----》插件apk文件
//LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo)
            Object loadedApk=getPackageInfoNoCheckMethod.invoke(currentActivityThread, applicationInfo, defaultCompatibilityInfo);



            String odexPath = Utils.getPluginOptDexDir(applicationInfo.packageName).getPath();
            String libDir = Utils.getPluginLibDir(applicationInfo.packageName).getPath();

            ClassLoader classLoader = new CustomClassLoader(path,odexPath,libDir,context.getClassLoader());
            Field mClassLoaderField = loadedApk.getClass().getDeclaredField("mClassLoader");
            mClassLoaderField.setAccessible(true);
            mClassLoaderField.set(loadedApk,classLoader);
            WeakReference weakReference = new WeakReference(loadedApk);

//     最终目的  是要替换ClassLoader  不是替换LoaderApk
            mPackages.put(applicationInfo.packageName,weakReference);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 得到 ApplicationInfo
     * PackageParser->generateApplicationInfo
     * @param context
     * @param path
     * @return
     */
    private ApplicationInfo   parseReceivers(Context context, String path) {

//        Package对象
//        PackageParser pp = new PackageParser();

//        PackageParser.Package  pkg = pp.parsePackage(scanFile, parseFlags);


        try {

            Class   packageParserClass = Class.forName("android.content.pm.PackageParser");
            Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class);
            Object packageParser = packageParserClass.newInstance();
            Object packageObj=  parsePackageMethod.invoke(packageParser, new File(path), PackageManager.GET_ACTIVITIES);

//



            Class<?> componentClass = Class.forName("android.content.pm.PackageParser$Component");
            Field intentsField = componentClass.getDeclaredField("intents");


            // 调用generateActivityInfo 方法, 把PackageParser.Activity 转换成
            Class<?> packageParser$ActivityClass = Class.forName("android.content.pm.PackageParser$Activity");
//            generateActivityInfo方法
            Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState");
            Object defaltUserState= packageUserStateClass.newInstance();
            Method generateReceiverInfo = packageParserClass.getDeclaredMethod("generateActivityInfo",
                    packageParser$ActivityClass, int.class, packageUserStateClass, int.class);
            Class<?> userHandler = Class.forName("android.os.UserHandle");
            Method getCallingUserIdMethod = userHandler.getDeclaredMethod("getCallingUserId");
            int userId = (int) getCallingUserIdMethod.invoke(null);
//目的     generateApplicationInfo  方法  生成  ApplicationInfo
            // 需要调用 android.content.pm.PackageParser#generateActivityInfo(android.content.pm.ActivityInfo, int, android.content.pm.PackageUserState, int)
            //      generateApplicationInfo
            Method generateApplicationInfoMethod = packageParserClass.getDeclaredMethod("generateApplicationInfo",
                    packageObj.getClass(),
                    int.class,
                    packageUserStateClass);
            ApplicationInfo applicationInfo= (ApplicationInfo) generateApplicationInfoMethod.invoke(packageParser, packageObj, 0, defaltUserState);

            applicationInfo.sourceDir = path;
            applicationInfo.publicSourceDir = path;
            return applicationInfo;
            //generateActivityInfo
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }


}
